﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using JESAI.Core.Extensions;

namespace JESAI.Core.Extensions
{
    /// <summary>
    /// MethodInfo 的扩展类
    /// </summary>
    public static class MethodInfoExtensions
    {
        private static ConcurrentDictionary<(MethodInfo method, string map), string> methodNamesCache = new ConcurrentDictionary<(MethodInfo method, string map), string>();
        private static ConcurrentDictionary<MethodInfo, (string name, List<(string genetypeName, bool isGenericParameter, Type geneType)> genericParameters)> methodNamesCache2 = new ConcurrentDictionary<MethodInfo, (string name, List<(string genetypeName, bool isGenericParameter, Type geneType)> genericParameters)>();

        /// <summary>
        /// 获取方法的详细描述，返回格式: "返回值类型 类名.方法名&lt;泛型列表&gt;(参数列表)"，示例：
        /// <list type="bullet">
        /// <item>typeof(Person).GetMethod("Show") => string TestNamespace.Person.Show(int x, int y)</item>
        /// </list>
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <returns></returns>
        public static string GetMethodFullName(this MethodInfo methodInfo) => methodInfo.GetMethodFullName(null);

        private static string GetMethodFullName(this MethodInfo methodInfo, Dictionary<string, string> genericMap)
        {
            if (methodInfo == null) return null;
            var map = "";
            if (genericMap.EnumerableIsNotNullOrEmpty())
            {
                var keys = genericMap.Keys.OrderBy(i => i);
                foreach (var item in keys)
                {
                    map += $",{item}={genericMap[item]}";
                }
                map = map.Trim(',');
            }
            return methodNamesCache.GetOrAdd((methodInfo, map), _ =>
            {
                var className = methodInfo.DeclaringType.GetClassFullName(genericMap);
                var methodName = methodInfo.Name;
                var returnName = methodInfo.ReturnType.GetClassFullName(genericMap).ToString();
                var genes = "";

                if (methodInfo.IsGenericMethod)
                {
                    //泛型方法
                    var types = methodInfo.GetGenericArguments();
                    genes = types.Select(i => i.GetClassFullName(genericMap)).ToStringSeparated(", ");
                    genes = $"<{genes}>";
                }

                var arguments = methodInfo.GetParameters();
                var arr = new string[arguments.Length];
                for (int i = 0; i < arguments.Length; i++)
                {
                    var parameter = arguments[i];
                    var parameterTypeName = parameter.ParameterType.GetClassFullName(genericMap);
                    var parameterName = parameter.Name;
                    arr[i] = $"{parameterTypeName} {parameterName}";
                }
                return $"{returnName} {className}.{methodName}{genes}({arr.ToStringSeparated(", ")})";
            });
        }

        /// <summary>
        /// 获取方法的详细描述，返回格式: "返回值类型 类名.方法名&lt;泛型列表&gt;(参数列表)"，示例：
        /// <list type="bullet">
        /// <item>typeof(Person).GetMethod("Show") => string TestNamespace.Person&lt;T>.Show&lt;T2>(T t1,T t2, int x),[("T", false, typeof(int)), ("T2", false, typeof(T2))]</item>
        /// </list>
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <returns></returns>
        public static (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) GetMethodGenericFullName(this MethodInfo methodInfo)
        {
            if (methodInfo == null) return (null, null);
            return methodNamesCache2.GetOrAdd(methodInfo, _ =>
             {
                 //获取方法的声明类
                 var declareType = methodInfo.DeclaringType;
                 //声明类的泛型具化字典
                 var (typeName, parasMap) = declareType.GetClassGenericFullName();
                 var list = parasMap.ToList();
                 var dic = new Dictionary<string, string>();
                 list.ForEach(i => dic.Add(i.name, i.type.GetClassFullName()));

                 //找到原始的定义: 泛型类模板以及它的泛型方法定义
                 //代表泛型类具化后的泛型方法定义
                 MethodInfo defineMidMethod = null;
                 //代表泛型类模板
                 Type topDeclareType = null;
                 if (methodInfo.IsGenericMethodDefinition)
                 {
                     //此方法是一个方法的泛型定义,如: void Show<T>();
                     defineMidMethod = methodInfo;
                 }
                 else if (methodInfo.IsGenericMethod)
                 {
                     //此方法就是一个具化的泛型方法,如: void Show<int>();
                     defineMidMethod = methodInfo.GetGenericMethodDefinition();
                 }
                 else
                 {
                     //非泛型方法
                     defineMidMethod = methodInfo;
                 }
                 topDeclareType = declareType.IsGenericType || declareType.IsGenericTypeDefinition ? declareType.GetGenericTypeDefinition() : declareType;

                 //找到最终的方法定义
                 var ms = topDeclareType.GetMethods().Where(i => i.Name == methodInfo.Name && i.GetParameters().Length == methodInfo.GetParameters().Length && i.IsStatic == methodInfo.IsStatic).ToList();

                 MethodInfo srcMethodDefine = null;
                 if (ms.Count == 1)
                 {
                     srcMethodDefine = ms[0];
                 }
                 else
                 {
                     //一个一个比对
                     var midName = defineMidMethod.GetMethodFullName();
                     for (int i = 0; i < ms.Count; i++)
                     {
                         var m = ms[i];
                         var tmp = m.GetMethodFullName(dic);
                         if (tmp == midName)
                         {
                             srcMethodDefine = m;
                         }
                     }
                 }
                 var name = srcMethodDefine.GetMethodFullName();
                 var defines = srcMethodDefine.GetGenericArguments();
                 var args = methodInfo.GetGenericArguments();
                 for (int j = 0; j < args.Length; j++)
                 {
                     var arg = args[j];
                     list.Add((defines[j].Name, methodInfo.IsGenericMethodDefinition, arg));
                 }
                 return (name, list);
             });
        }
    }
}
